package cn.xinfei.xdecision.common.service.enginex.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.xinfei.xdecision.common.model.component.dependency.resp.DecisionComponentDependencyResp;
import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.component.dependency.EngineNodeComponentRelService;
import cn.xinfei.xdecision.common.utils.constant.CommonConst;
import cn.xinfei.xdecision.common.utils.constant.SysConstant;
import cn.xinfei.xdecision.common.utils.constant.enginex.EngineNodeConst;
import cn.xinfei.xdecision.common.utils.enums.enginex.EngineVersionStatusEnum;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.util.SpringContextUtil;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.xinfei.xdecision.common.utils.constant.enginex.DecisionFlowCheckMessageConst.BASE_RULE_DECISION_FLOW_CYCLIC_DEPENDENCY;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.*;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

/**
 * Created by maying on 2023/04/25.
 **/
@Slf4j
public class NodeUtil {

    /**
     * 决策节点：规则库、评分卡、决策树、决策表、决策选项、子引擎
     */
    private static final List<String> STRATEGY_NODE_TYPES = Arrays.asList(
            NodeTypeEnum.RULE_BASE.getType(),
            NodeTypeEnum.SCORECARD.getType(),
            NodeTypeEnum.DECISION_TREE.getType(),
            NodeTypeEnum.DECISION_TABLES.getType(),
            NodeTypeEnum.DECISION.getType(),
            NodeTypeEnum.CHILD_ENGINE.getType()
    );

    /**
     * 是否决策节点
     */
    public static boolean isStrategyNode(String nodeType) {
        return STRATEGY_NODE_TYPES.contains(nodeType);
    }

    /**
     * 分支节点：分组、分流、并行、聚合、冠军、人行征信
     */
    private static final List<String> BRANCH_NODE_TYPES = Arrays.asList(
            NodeTypeEnum.CLASSIFY.getType(),
            NodeTypeEnum.SANDBOX.getType(),
            NodeTypeEnum.PARALLEL.getType(),
            NodeTypeEnum.AGGREGATION.getType(),
            NodeTypeEnum.CHAMPION_CHALLENGE.getType(),
            NodeTypeEnum.CREDIT_REPORT.getType()
    );

    /**
     * 是否分支节点
     */
    public static boolean isBranchNode(String nodeType) {
        return BRANCH_NODE_TYPES.contains(nodeType);
    }

    /**
     * nodeJson允许为空的节点，即不需要配置节点属性。
     * 开始、结束、聚合
     */
    public static final List<String> IGNORE_NODE_JSON_CHECK_NODE_TYPES = Arrays.asList(
            NodeTypeEnum.START.getType(),
            NodeTypeEnum.END.getType(),
            NodeTypeEnum.AGGREGATION.getType(),
            NodeTypeEnum.CREDIT_REPORT.getType()
    );

    /**
     * 将nodeList转化为Map，以nodeCode做Key
     */
    public static Map<String, EngineNode> convertNodeList2MapNodeCodeKey(List<EngineNode> nodes) {
        if (nodes == null || nodes.isEmpty()) {
            return new HashMap<>();
        }
        return nodes.stream().collect(Collectors.toMap(EngineNode::getNodeCode, Function.identity()));
    }

    /**
     * 将nodeList转化为Map，以nodeId做Key
     */
    public static Map<Long, EngineNode> convertNodeList2Map(List<EngineNode> nodes) {
        if (nodes == null || nodes.isEmpty()) {
            return new HashMap<>();
        }
        return nodes.stream().collect(Collectors.toMap(EngineNode::getId, Function.identity()));
    }

    /**
     * 获取从当前节点(不含)至结束节点(不含),sortNodeList中不包含startNode返回空列表
     *
     * @param startNode    当前节点
     * @param sortNodeList 当前节点所在的路径分支，经过排序的
     * @return 截取从(startNode, endNode)的节点
     */
    public static List<EngineNode> subNodesRight(EngineNode startNode, List<EngineNode> sortNodeList) {
        for (int i = 0; i < sortNodeList.size(); i++) {
            EngineNode node = sortNodeList.get(i);
            if (node.getId().equals(startNode.getId())) {
                if (i >= sortNodeList.size() - 2) {//当前节点是最后两个节点之一，返回空列表
                    return new LinkedList<>();
                }
                return sortNodeList.subList(i + 1, sortNodeList.size() - 1);
            }
        }
        return new LinkedList<>();
    }

    /**
     * 获取从开始节点(不含)至当前节点(不含)，sortNodeList中不包含startNode返回空列表
     *
     * @param startNode    当前节点
     * @param sortNodeList 当前节点所在的路径分支，经过排序的
     * @return 截取从(startNode, endNode)的节点
     */
    public static List<EngineNode> subNodesLeft(EngineNode startNode, List<EngineNode> sortNodeList) {
        for (int i = 0; i < sortNodeList.size(); i++) {
            EngineNode node = sortNodeList.get(i);
            if (node.getId().equals(startNode.getId())) {
                return sortNodeList.subList(0, i);
            }
        }
        return new LinkedList<>();
    }

    /**
     * 获取从当前节点(不含)至nodeTypeEnum.getNodeType()类型的节点(不含)list
     * 1.当前节点下一节点为结束节点，返回空列表
     * 2.当前节点后无指定类型的节点，返回空列表
     */
    public static List<EngineNode> subNodesDesignatedEndNodeType(EngineNode startNode, List<EngineNode> sortNodeList, NodeTypeEnum nodeTypeEnum) {
        List<EngineNode> rightNodes = subNodesRight(startNode, sortNodeList);
        if (rightNodes.isEmpty()) {
            return new LinkedList<>();
        }
        Integer index = null;
        for (int i = 0; i < rightNodes.size(); i++) {
            EngineNode node = rightNodes.get(i);
            if (node.getNodeType().equals(nodeTypeEnum.getType())) {
                index = i;
            }
        }

        if (index == null) {
            return new LinkedList<>();
        }
        return rightNodes.subList(0, index);
    }

    /**
     * 是否包含分支节点
     */
    public static boolean containsBranchNode(List<EngineNode> nodes) {
        if (CollectionUtil.isEmpty(nodes)) {
            return false;
        }
        for (EngineNode node : nodes) {
            if (isBranchNode(node.getNodeType())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否包含指定类型的节点
     */
    public static boolean containsDesignatedTypeNode(List<EngineNode> nodes, NodeTypeEnum nodeTypeEnum) {
        if (CollectionUtil.isEmpty(nodes)) {
            return false;
        }
        for (EngineNode node : nodes) {
            if (nodeTypeEnum.getType().equals(node.getNodeType())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 是否包含策略节点
     */
    public static boolean containsStrategyNode(List<EngineNode> nodes) {
        if (CollectionUtil.isEmpty(nodes)) {
            return false;
        }
        for (EngineNode node : nodes) {
            if (isStrategyNode(node.getNodeType())) {
                return true;
            }
        }
        return false;
    }


    /**
     * 是否包含开始结束节点，且各为1
     */
    public static boolean containsStartEndNode(List<EngineNode> nodes) {
        List<String> startAndEndNodes = nodes.stream().filter(node ->
                node.getNodeType().equals(NodeTypeEnum.START.getType()) || node.getNodeType().equals(NodeTypeEnum.END.getType())
        ).map(EngineNode::getNodeType).collect(Collectors.toList());

        return startAndEndNodes != null && startAndEndNodes.size() == 2;
    }

    /**
     * 是否包含孤立节点
     */
    public static boolean containsIsolatedNode(List<EngineNode> nodes) {
        if (CollectionUtil.isEmpty(nodes)) {
            return false;
        }
        List<String> isolatedNodes = nodes.stream().filter(node ->
                //开始节点无子节点
                (node.getNodeType().equals(NodeTypeEnum.START.getType()) && StringUtil.isBlank(node.getNextNodeCodes()))
                        //结束节点无父节点
                        || (node.getNodeType().equals(NodeTypeEnum.END.getType()) && StringUtil.isBlank(node.getParentId()) || "-1".equals(node.getParentId()))
                        //存在既无子也无父的孤儿节点
                        || StringUtil.isBlank(node.getNextNodeCodes()) && (StringUtil.isBlank(node.getParentId()) || "-1".equals(node.getParentId()))
        ).map(EngineNode::getNodeType).collect(Collectors.toList());
        log.info("孤立节点:{}", JSONObject.toJSONString(isolatedNodes));
        return isolatedNodes != null && isolatedNodes.size() > 0;
    }

    /**
     * 深度优先获取树的所有路径
     *
     * @param node     节点
     * @param allNodes 树的所有节点信息
     * @return 所有可能的分支路径
     */
    public static List<List<EngineNode>> allPaths(EngineNode root, Map<String, EngineNode> allNodes) {
        List<List<EngineNode>> paths = new ArrayList<>();
        if (root == null) {
            return paths;
        }
        dfs(root, allNodes, new ArrayList<>(), paths);
        log.info("决策流开始节点:{},所有路径:{}", JSONObject.toJSONString(root), JSONObject.toJSONString(paths, SerializerFeature.DisableCircularReferenceDetect));
        return paths;
    }

    /**
     * 深度优先获取树的所有路径
     *
     * @param node     节点
     * @param allNodes 树的所有节点信息
     * @param path     路径
     * @param paths    存放所有路径的list
     */
    private static void dfs(EngineNode node, Map<String, EngineNode> allNodes, List<EngineNode> path, List<List<EngineNode>> paths) {
        path.add(node);
        if (StringUtils.isEmpty(node.getNextNodeCodes())) {
            paths.add(new ArrayList<>(path));  // 拷贝一份path添加到结果中
        } else {
            String childNodeCodes = node.getNextNodeCodes();
            if (!StringUtils.isEmpty(childNodeCodes)) {
                List<String> nodeCodes = Arrays.asList(childNodeCodes.split(","));
                for (String nodeCode : nodeCodes) {
                    EngineNode childNode = allNodes.get(nodeCode);
                    if (childNode == null) {
                        log.error("{}节点配置信息有误,当前版本中未查到子节点:{}", node, nodeCode);
                        throw exception(NODE_INFO_ERROR, "当前版本中未查到子节点:" + nodeCode);
                    }
                    dfs(childNode, allNodes, path, paths);
                }
            }
        }

        path.remove(path.size() - 1);  // 回溯
    }

    /**
     * 判断是否是环形
     *
     * @return 前驱节点集合
     */
    public static void recursionEngines(String engineCode, Long versionId) {
        findAllParentEngineCodesCount(engineCode, versionId, engineCode);
        //findAllParentEngineCodesCount(engineCode, Sets.newHashSet(engineCode));
    }

    /**
     * @param allCodes            全部引擎集合
     * @param circularEngineCodes 前驱节点code集合
     * @param engineCodes         需要查询的引擎集合
     */
//    public static void recursionEngines(Set<String> allCodes, Set<String> circularEngineCodes, Set<String> engineCodes) {
//        if (CollectionUtils.isEmpty(engineCodes)) {
//            return;
//        }
//        EngineNodeComponentRelService engineNodeComponentRelService = SpringContextUtil.getBean(EngineNodeComponentRelService.class);
//        List<DecisionComponentDependencyResp> dependencyRespList = engineNodeComponentRelService
//                .listByEngineNodeCodes(new ArrayList<>(engineCodes), NodeTypeEnum.CHILD_ENGINE.getCode());
//        if (CollectionUtils.isEmpty(dependencyRespList)) {
//            return;
//        }
//        //子引擎
//        Set<String> engineCodeList = dependencyRespList.stream()
//                .map(dependencyResp -> dependencyResp.getEngineNodeCode()
//                        .substring(0, dependencyResp.getEngineNodeCode().lastIndexOf(SysConstant.UNDERLINE))).collect(Collectors.toSet());
//        //先复制
//        Set<String> allCodesCopy = Sets.newHashSet(allCodes);
//        //取交集
//        allCodesCopy.retainAll(engineCodeList);
//        //不为空，放入环形set
//        if (!CollectionUtils.isEmpty(allCodesCopy)) {
//            circularEngineCodes.addAll(allCodesCopy);
//        }
//        //全部引擎code
//        allCodes.addAll(engineCodeList);
//        //排除前驱节点，继续递归
//        engineCodeList.removeAll(circularEngineCodes);
//        //递归
//        recursionEngines(allCodes, circularEngineCodes, engineCodeList);
//    }

    /**
     * 查询非自身引擎之外的循环依赖
     */
    public static void findAllParentEngineCodesCount(String engineCode, Set<String> engineCodes) {
        EngineNodeComponentRelService engineNodeComponentRelService = SpringContextUtil.getBean(EngineNodeComponentRelService.class);
        List<DecisionComponentDependencyResp> dependencyRespList = engineNodeComponentRelService
                .listByEngineNodeCodes(Collections.singletonList(engineCode), NodeTypeEnum.CHILD_ENGINE.getCode(),
                        EngineVersionStatusEnum.DISABLE.getStatus());
        if (CollectionUtils.isEmpty(dependencyRespList)) {
            return;
        }
        //父引擎
        List<String> parentEngineCodeList = dependencyRespList.stream()
                .map(dependencyResp -> dependencyResp.getEngineNodeCode()
                        .substring(0, dependencyResp.getEngineNodeCode().lastIndexOf(SysConstant.UNDERLINE))).distinct()
                .collect(Collectors.toList());
        for (String parentEngineCode : parentEngineCodeList) {
            Set<String> enCodeSet = Sets.newHashSet(engineCodes);
            if (enCodeSet.contains(parentEngineCode)) {
                throw exception(DECISION_FLOW_CHECK_ERROR,
                        BASE_RULE_DECISION_FLOW_CYCLIC_DEPENDENCY + ",循环依赖决策流编码:" + parentEngineCode);
            }
            enCodeSet.add(parentEngineCode);
            findAllParentEngineCodesCount(parentEngineCode, enCodeSet);
        }
    }

    /**
     * 查询自身引擎的循环依赖
     */
    public static void findAllParentEngineCodesCount(String originalEngineCode, Long versionId, String parentEngineCode) {
        EngineNodeComponentRelService engineNodeComponentRelService = SpringContextUtil.getBean(EngineNodeComponentRelService.class);
        List<DecisionComponentDependencyResp> dependencyRespList = engineNodeComponentRelService
                .listByEngineNodeCodes(Collections.singletonList(parentEngineCode), NodeTypeEnum.CHILD_ENGINE.getCode(),
                        null);
        if (CollectionUtils.isEmpty(dependencyRespList)) {
            return;
        }
        //父引擎
        dependencyRespList.forEach(parentEngine -> {
            String code = parentEngine.getEngineNodeCode()
                    .substring(0, parentEngine.getEngineNodeCode().lastIndexOf(SysConstant.UNDERLINE));
            //如果父引擎是自己，并且版本号和上线的版本号一致，则提示循环依赖
            if (Objects.equals(originalEngineCode, code) && Objects.equals(parentEngine.getEngineVersionId(), versionId)) {
                throw exception(DECISION_FLOW_CHECK_ERROR,
                        BASE_RULE_DECISION_FLOW_CYCLIC_DEPENDENCY + ",循环依赖决策流编码:" + parentEngineCode);
            }
            if (Objects.equals(parentEngine.getEngineVersionStatus(), EngineVersionStatusEnum.DISABLE.getStatus())) {
                return;
            }
            findAllParentEngineCodesCount(originalEngineCode, versionId, code);
        });
    }

    public static void addNextNode(EngineNode sourceNode, String nextNodeCode) {
        String sourceNodeNextNodeCodes = sourceNode.getNextNodeCodes();
        if (StringUtil.isBlank(sourceNodeNextNodeCodes) || CommonConst.SYMBOL_COMMA.equals(sourceNodeNextNodeCodes.trim())) {
            sourceNode.setNextNodeCodes(nextNodeCode);
            return;
        }

        List<String> nextNodesList = Arrays.asList(sourceNodeNextNodeCodes.split(CommonConst.SYMBOL_COMMA));
        if (nextNodesList.contains(nextNodeCode)) {
            log.error("{}节点配置信息nextNodeCodes中已包含该节点:{}", sourceNode.getNodeCode(), nextNodeCode);
            throw exception(ENGINE_NODE_LINK_ERROR_003);
        }
        sourceNodeNextNodeCodes = sourceNodeNextNodeCodes.concat(CommonConst.SYMBOL_COMMA).concat(nextNodeCode);
        sourceNode.setNextNodeCodes(sourceNodeNextNodeCodes);
    }

    public static void addParentId(EngineNode targetNode, String parentId) {
        String targetNodeParentId = targetNode.getParentId();
        if (StringUtil.isBlank(targetNodeParentId) || CommonConst.SYMBOL_COMMA.equals(targetNodeParentId.trim())) {
            targetNode.setParentId(parentId);
            return;
        }

        List<String> parentIds = Arrays.asList(targetNodeParentId.split(CommonConst.SYMBOL_COMMA));
        if (parentIds.contains(parentId)) {
            log.error("{}节点配置信息parentId中已包含该节点:{}", targetNode.getNodeCode(), parentId);
            throw exception(ENGINE_NODE_LINK_ERROR_003);
        }

        targetNodeParentId = targetNodeParentId.concat(CommonConst.SYMBOL_COMMA).concat(parentId);
        targetNode.setParentId(targetNodeParentId);
    }

    public static void removeNextNodeCode(EngineNode sourceNode, String nextNodeCode) {
        String sourceNodeNextNodeCodes = sourceNode.getNextNodeCodes();
        if (StringUtil.isBlank(sourceNodeNextNodeCodes) || CommonConst.SYMBOL_COMMA.equals(sourceNodeNextNodeCodes.trim())) {
            log.error("{}节点配置信息nextNodeCodes中不包含该节点:{}，删除异常.", sourceNode.getNodeCode(), nextNodeCode);
            throw exception(ENGINE_NODE_LINK_ERROR_003);
        }
        List<String> nextNodesList = Arrays.asList(sourceNodeNextNodeCodes.split(CommonConst.SYMBOL_COMMA));
        List<String> newList = new LinkedList<>(nextNodesList);
        newList.remove(nextNodeCode);
        sourceNodeNextNodeCodes = StringUtils.collectionToDelimitedString(newList, ",");
        sourceNode.setNextNodeCodes(sourceNodeNextNodeCodes);
    }

    public static void removeParentId(EngineNode targetNode, String parentId) {
        String targetNodeParentId = targetNode.getParentId();
        if (StringUtil.isBlank(targetNodeParentId) || CommonConst.SYMBOL_COMMA.equals(targetNodeParentId.trim())) {
            log.error("{}节点配置信息parentId中不包含该节点:{}，删除异常.", targetNode.getParentId(), parentId);
            throw exception(ENGINE_NODE_LINK_ERROR_003);
        }

        List<String> nextNodesList = Arrays.asList(targetNodeParentId.split(CommonConst.SYMBOL_COMMA));
        List<String> newList = new LinkedList<>(nextNodesList);
        newList.remove(parentId);
        targetNodeParentId = StringUtils.collectionToDelimitedString(newList, CommonConst.SYMBOL_COMMA);
        if (StringUtils.isEmpty(targetNodeParentId)) {
            targetNodeParentId = EngineNodeConst.REMOVE_LINK_PARENT_ID;
        }
        targetNode.setParentId(targetNodeParentId);
    }


}
